#include "Storage.h"
#include <regex>
#include <iostream>
#include <fstream>

Storage* Storage::instance_ = nullptr;

Storage::Storage() {
  if (readFromFile("agenda.data") == false) {
    userList_.clear();
    meetingList_.clear();
  }
}

Storage& Storage::getInstance(void) {
  if (!instance_) {
    instance_ = new Storage();
  }
  return *instance_;
}

Storage::~Storage() {
  sync();
}

bool Storage::sync(void) {
  return writeToFile("agenda.data");
}

void Storage::createUser(const User &user_) {
  userList_.push_back(user_);
}

std::list<User> Storage::queryUser(std::function<bool(const User &)> filter) {
  std::list<User> v;
  for (auto i : userList_) {
    if (filter(i))
      v.push_back(i);
  }
  return v;
}

int Storage::updateUser(std::function<bool(const User &)> filter,
                        std::function<void(User &)> switcher) {
  int result = 0;
  for (auto i : userList_) {
    if (filter(i)) {
      switcher(i);
      result++;
    }
  }
  return result;
}

int Storage::deleteUser(std::function<bool(const User &)> filter) {
  int result = 0;
  for (auto i = userList_.begin(); i != userList_.end(); ++i) {
    if (filter(*i)) {
      auto tmpit = i;
      --i;
      userList_.erase(tmpit);
      result++;
    }
  }
  return result;
}

void Storage::createMeeting(const Meeting &meeting_) {
  meetingList_.push_back(meeting_);
}

std::list<Meeting> Storage::queryMeeting(
    std::function<bool(const Meeting &)>filter) {
  std::list<Meeting> v;
  for (auto i : meetingList_) {
    if (filter(i))
      v.push_back(i);
  }
  return v;
}

int Storage::updateMeeting(std::function<bool(const Meeting &)> filter,
                           std::function<void(Meeting &)> switcher) {
  int result = 0;
  for (auto i : meetingList_) {
    if (filter(i)) {
      switcher(i);
      result++;
    }
  }
  return result;
}

int Storage::deleteMeeting(std::function<bool(const Meeting &)> filter) {
  int result = 0;
  for (auto i = meetingList_.begin(); i != meetingList_.end(); ++i) {
    if (filter(*i)) {
      auto tmpit = i;
      --i;
      meetingList_.erase(tmpit);
      result++;
    }
  }
  return result;
}

std::string numberPattern(std::string name) {
    return name + "\\s*:\\s*(\\d+)";
}

std::string wordPattern(std::string name) {
    return name + "\\s*:\\s*\"(\\S+)\"";
}

bool getWord(std::string source, std::string name, std::string &word) {
    std::smatch matches;
    if (std::regex_search(source, matches, std::regex(wordPattern(name)))) {
        word = matches[1].str();
        return true;
    }
    else {
        return false;
    }
}

bool getNumber(std::string source, std::string name, int &number) {
    std::smatch matches;
    if (std::regex_search(source, matches, std::regex(wordPattern(name)))) {
        std::string temp = matches[1].str();
        number = 0;
        for (int i = 0; i < temp.size(); i++) {
            number = number * 10 + temp[i] - '0';
        }
        return true;
    }
    else {
        return false;
    }
}

Meeting addMeeting(std::string line) {
    std::string sponsor;
    std::string participator;
    std::string startDate;
    std::string endDate;
    std::string meetingTitle;
    getWord(line, "sponsor", sponsor);
    getWord(line, "participator", participator);
    getWord(line, "st_date", startDate);
    getWord(line, "ed_date", endDate);
    getWord(line, "title", meetingTitle);
    return Meeting(sponsor, participator,
                   Date::stringToDate(startDate),
                   Date::stringToDate(endDate), meetingTitle);
}

User addUser(std::string line) {
    std::string name;
    std::string password;
    std::string email;
    std::string phone;
    getWord(line, "name", name);
    getWord(line, "pwd", password);
    getWord(line, "phone", phone);
    getWord(line, "email", email);
    return User(name, password, email, phone);
}

bool Storage::readFromFile(const char *fpath) {
    std::ifstream fin(fpath);
    if (!fin.good()) {
        return false;
    }
    std::string title;
    std::string word;
    std::string type;
    int total;
    while (fin >> title) {
        if (getWord(title, "collection", type)) {
            getNumber(title, "total", total);
            for (int i = 0; i < total; i++) {
                std::string line;
                fin >> line;
                if (type == "User") {
                    userList_.push_back(addUser(line));
                }
                else if (type == "Meeting") {
                    meetingList_.push_back(addMeeting(line));
                }
            }
        }
        else {
            return false;
        }
    }
    return true;
}

bool Storage::writeToFile(const char *fpath) {
    std::ofstream fout(fpath);
    if (!fout.good()) {
        return false;
    }
    fout << "{collection:\"User\",total:" << userList_.size() << "}" << std::endl;
    for (auto user : userList_) {
        fout << "{";
        fout << "name:\"" << user.getName() << "\",";
        fout << "pwd:\"" << user.getPassword() << "\",";
        fout << "email:\"" << user.getEmail() << "\",";
        fout << "phone:\"" << user.getPhone() << "\"";
        fout << "}" << std::endl;
    }
    fout << "{collection:\"Meeting\",total:" << meetingList_.size() << "}" << std::endl;
    for (auto meeting : meetingList_) {
        fout << "{";
        fout << "sponsor:\"" << meeting.getSponsor() << "\",";
        fout << "participator:\"" << meeting.getParticipator() << "\",";
        fout << "st_date:\"" << Date::dateToString(meeting.getStartDate()) << "\",";
        fout << "ed_date:\"" << Date::dateToString(meeting.getEndDate()) << "\",";
        fout << "title:\"" << meeting.getTitle() << "\"";
        fout << "}" << std::endl;
    }
    return true;
    
}
